package com.ecoolex.hotel.saas.core.service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import com.ecoolex.hotel.saas.enums.PresaleCouponOrderPaymentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.util.BigDecimalUtil;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;
import com.ecoolex.framework.common.util.DateUtil;
import com.ecoolex.framework.common.util.MessageFormatter;
import com.ecoolex.framework.common.util.RandomUtil;
import com.ecoolex.framework.modularize.api.auth.user.ConsumerMemberManager;
import com.ecoolex.framework.modularize.api.auth.user.ConsumerWeiXinMember;
import com.ecoolex.hotel.saas.core.external.WeChatPayExternalService;
import com.ecoolex.hotel.saas.core.external.WechatEntPayExternalService;
import com.ecoolex.hotel.saas.core.manager.PaymentOrderManager;
import com.ecoolex.hotel.saas.core.manager.PaymentOrderRecordManager;
import com.ecoolex.hotel.saas.core.manager.PresaleCouponOrderManager;
import com.ecoolex.hotel.saas.core.manager.StoreWeixinConfigManager;
import com.ecoolex.hotel.saas.entity.PaymentOrder;
import com.ecoolex.hotel.saas.entity.PaymentOrderRecord;
import com.ecoolex.hotel.saas.entity.PresaleCouponOrder;
import com.ecoolex.hotel.saas.entity.StoreWeixinConfig;
import com.ecoolex.hotel.saas.enums.PaymentOrderRecordOperType;
import com.ecoolex.hotel.saas.enums.PaymentOrderRecordResultStatus;
import com.ecoolex.hotel.saas.enums.PaymentOrderStatus;
import com.github.binarywang.wxpay.bean.WxPayApiData;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;

@Service
public class WeChatPayService {

	Logger log = LoggerFactory.getLogger(WeChatPayService.class);
	
	@Autowired
	private WeChatPayExternalService weChatPayExternalService;
	
	@Autowired
	private WechatEntPayExternalService wechatEntPayExternalService;
	
	@Autowired
    private ConsumerMemberManager consumerMemberManager;
	
	@Autowired
	private PaymentOrderManager paymentOrderManager;
	
	@Autowired
    private PaymentOrderRecordManager paymentOrderRecordManager;
	
	@Autowired
	private PresaleCouponOrderManager presaleCouponOrderManager;
	
	@Autowired
	private StoreWeixinConfigManager storeWeixinConfigManager;
	
	/**
	 * 发起微信公众号支付/小程序支付
	 * @param order
	 * @param appId
	 * @param ipAdress
	 * @param createUser
	 * @return
	 */
	public Map payApply(PresaleCouponOrder order, String appId, String ipAdress, String createUser) {
		return paymentOrderManager.couponOrderPayApply(order, PresaleCouponOrderPaymentType.WECHAT_PAY.real(), createUser, (paymentOrder, record) -> {
			// 拼接流水号
	        String orderSn = paymentOrder.getBizType() + '-' + paymentOrder.getOrderNo();
	        // 获取用户的openId
	        List<ConsumerWeiXinMember> weixinList = consumerMemberManager.queryWeiXinByUserId(order.getUserId());
	        BizAssert.isTrue(Check.notNullOrEmpty(weixinList), ResultCode.BIZ.build(11), "用户未绑定微信, 无法使用微信支付");
	        // 发起支付
	        try {
	            BigDecimal totalPrice = paymentOrder.getPrepaidPrice();
	            //  设置支付类型
	            record.setCurrencyType("CNY");
	            record.setTransAmount(totalPrice);
	            StoreWeixinConfig weixinConfig = storeWeixinConfigManager.getStoreWeixinConfig(order.getStoreId());
	            BizAssert.isTrue(Check.notNull(weixinConfig),ResultCode.VERIFY.build(12), "商户微信支付未配置，无法发起微信支付");
	            return weChatPayExternalService.prepay(weixinList.get(0).getOpenId(),
	                    orderSn,
	                    MessageFormatter.arrayFormat("订单：{}", order.getOrderNo()),
	                    "CNY",
	                    BigDecimalUtil.dollarToCent(record.getTransAmount()).intValue(),
	                    ipAdress,
	                    weixinConfig);
	        } catch (WxPayException e) {
	            throw new RuntimeException(e.getMessage(), e);
	        } finally {
	            // 记录错误日志
	            WxPayApiData wxApiData = weChatPayExternalService.getWxApiData();
	            if (Check.notNull(wxApiData)) {
	                record.setRequest(wxApiData.getRequestData());
	                record.setResponse(wxApiData.getResponseData());
	                record.setErrorMessage(wxApiData.getExceptionMsg());
	            }
	        }
		});
	}
	
	/**
	 * 支付通知
	 * @param body
	 * @param createUser
	 * @return
	 */
	public String payNotify(String body, String createUser) {
		try {
            WxPayOrderNotifyResult notifyResult = weChatPayExternalService.parseOrderNotifyResult(body);
            // 订单号
            String tradeNo = notifyResult.getOutTradeNo();
            int index = tradeNo.indexOf('-');
            String bizType = tradeNo.substring(0, index);
            String orderNo = tradeNo.substring(index + 1);
            PaymentOrder order = paymentOrderManager.getByBizTypeAndOrderNo(bizType, orderNo);
            BizAssert.isTrue(Check.notNull(order), "支付系统中不存在该订单");

            // 检查支付订单的状态, 并生成相应记录
            PaymentOrderRecord log = paymentOrderRecordManager.create(order, PresaleCouponOrderPaymentType.WECHAT_PAY.real(), PaymentOrderRecordOperType.ORDER_NOTIFY);
            try {
                // 检查通知结果
            	weChatPayExternalService.checkNotifyResult(notifyResult);
                // 如果成功的话, 记录数据
                log.setPaymentType(PresaleCouponOrderPaymentType.WECHAT_PAY.real());
                log.setCurrencyType(notifyResult.getFeeType());
                log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(notifyResult.getTotalFee())));
                log.setTransNo(notifyResult.getTransactionId());
                log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", notifyResult.getTimeEnd()));
                log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
            } catch (Exception e) {
                log.setErrorMessage(e.getMessage());
                log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            } finally {
                // 记录相应的日志
                log.setRequest(body);
                log.setResponse(weChatPayExternalService.notifyResult(true, "通知成功"));
                paymentOrderRecordManager.add(log, createUser);
            }

            paymentOrderManager.updatePayStatusAndNotify(order, log, createUser);

            return log.getResponse();
        } catch (Exception e) {
        	log.error("付款通知异常：",e);
            return weChatPayExternalService.notifyResult(false, e.getMessage());
        }
	}
	
	/*
     *检查支付状态
     */
    public void checkPayStatus(PaymentOrder order, String updateUser) {
        // 不是支付中状态不检查
        if (PaymentOrderStatus.PAY_APPLY.notEqual(order.getStatus())) {
            return;
        }
        PresaleCouponOrder presaleCouponOrder = presaleCouponOrderManager.getByOrderNo(order.getOrderNo());
        StoreWeixinConfig storeWeixinConfig = storeWeixinConfigManager.getStoreWeixinConfig(presaleCouponOrder.getStoreId());
        PaymentOrderRecord log = paymentOrderRecordManager.create(order, PresaleCouponOrderPaymentType.WECHAT_PAY.real(), PaymentOrderRecordOperType.ORDER_CHECK);
        try {
            // 拼接流水号
            String orderSn = order.getBizType() + '-' + order.getOrderNo();

            WxPayOrderQueryResult payResult = weChatPayExternalService.getPayResult(orderSn, storeWeixinConfig.getMchId());

            log.setCurrencyType(payResult.getFeeType());
            log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(payResult.getTotalFee())));
            log.setTransNo(payResult.getTransactionId());

            if (payResult.getTradeState().equalsIgnoreCase("USERPAYING")) {
                // 用户支付中
                log.setResultStatus(PaymentOrderRecordResultStatus.ONGOING.real());
            } else if (payResult.getTradeState().equalsIgnoreCase("SUCCESS")) {
                // 用户支付成功
                log.setTransTime(DateUtil.parseToDate("yyyyMMddHHmmss", payResult.getTimeEnd()));
                log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
            } else {
                // 其他原因全为失败
                log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            }

            paymentOrderManager.updatePayStatusAndNotify(order, log, updateUser);
        } catch (WxPayException e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            WxPayApiData data = weChatPayExternalService.getWxApiData();
            if (Check.notNull(data)) {
                log.setRequest(data.getRequestData());
                log.setResponse(data.getResponseData());
                log.setErrorMessage(data.getExceptionMsg());
            }
            // 记录调用详情
            paymentOrderRecordManager.add(log, updateUser);
        }
    }
    
    /*
     *	退款申请
     */
    public void refundApply(PaymentOrder order,BigDecimal refundPrice,String refundDesc,Integer storeId, PaymentOrderRecord record) {
        try {
        	record.setPaymentType(PresaleCouponOrderPaymentType.WECHAT_PAY.real());

            String orderSn = order.getBizType() + '-' + order.getOrderNo();
            String refundNo = "r-" + order.getBizType() + '-' + order.getOrderNo() + RandomUtil.randomInt(8);
            StoreWeixinConfig weixinConfig = storeWeixinConfigManager.getStoreWeixinConfig(storeId);
            BizAssert.isTrue(Check.notNull(weixinConfig),ResultCode.VERIFY.build(12), "商户微信支付未配置，无法发起微信退款");
            WxPayRefundResult result = weChatPayExternalService.refund(orderSn,
                    refundNo,
                    order.getPaymentCurrencyType(),
                    order.getPaymentPrice(),
                    refundPrice,
                    refundDesc,
                    weixinConfig.getMchId());

            record.setPaymentType(PresaleCouponOrderPaymentType.WECHAT_PAY.real());
            record.setCurrencyType(order.getPaymentCurrencyType());
            record.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(result.getRefundFee())));
            record.setTransNo(result.getRefundId());
            record.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
        } catch (WxPayException e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            WxPayApiData data = weChatPayExternalService.getWxApiData();
            if (Check.notNull(data)) {
            	record.setRequest(data.getRequestData());
            	record.setResponse(data.getResponseData());
            	record.setErrorMessage(data.getExceptionMsg());
            }
        }
    }
	
	/**
	 * 退款通知
	 * @param body
	 * @param createUser
	 * @return
	 */
	public String refundNotify(String body, String createUser) {
		try {
            WxPayRefundNotifyResult notifyResult = weChatPayExternalService.parseRefundNotifyResult(body);
            // 订单号
            String tradeNo = notifyResult.getReqInfo().getOutTradeNo();
            int index = tradeNo.indexOf('-');
            String bizType = tradeNo.substring(0, index);
            String orderNo = tradeNo.substring(index + 1);

            PaymentOrder order = paymentOrderManager.getByBizTypeAndOrderNo(bizType, orderNo);
            BizAssert.isTrue(Check.notNull(order), "支付系统中不存在该订单");

            // 检查支付订单的状态, 并生成相应记录
            PaymentOrderRecord log = paymentOrderRecordManager.create(order, PresaleCouponOrderPaymentType.WECHAT_PAY.real(), PaymentOrderRecordOperType.REFUND_NOTIFY);
            try {
                // 记录退款金额,退款单号
                log.setPaymentType(PresaleCouponOrderPaymentType.WECHAT_PAY.real());
                log.setCurrencyType(order.getPaymentCurrencyType());
                log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(notifyResult.getReqInfo().getRefundFee())));
                log.setTransNo(notifyResult.getReqInfo().getRefundId());

                if (notifyResult.getReqInfo().getRefundStatus().equalsIgnoreCase("SUCCESS")) {
                    log.setTransTime(DateUtil.parseToDate(DateUtil.DEFAULT_DATETIME_FORMAT,
                            notifyResult.getReqInfo().getSuccessTime()));
                    log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
                } else {
                    log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
                }
                order.setPaymentNo(log.getTransNo());
                order.setPaymentTime(log.getTransTime());
            } catch (Exception e) {
                log.setErrorMessage(e.getMessage());
                log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            } finally {
                // 记录相应的日志
                log.setRequest(notifyResult.toString());
                log.setResponse(weChatPayExternalService.notifyResult(true, "通知成功"));
                paymentOrderRecordManager.add(log, createUser);
            }

            paymentOrderManager.updateRefundStatusAndNotify(order, log, createUser);

            return log.getResponse();
        } catch (Exception e) {
        	log.error("退款通知异常：",e);
            return weChatPayExternalService.notifyResult(false, e.getMessage());
        }
	}
	
	/*
     *	检查退款状态
     */
    public void checkRefundStatus(PaymentOrder order, String updateUser) {
        // 不是支付中状态不检查
        if (PaymentOrderStatus.REFUND_APPLY.notEqual(order.getStatus())) {
            return;
        }
        PresaleCouponOrder presaleCouponOrder = presaleCouponOrderManager.getByOrderNo(order.getOrderNo());
        StoreWeixinConfig storeWeixinConfig = storeWeixinConfigManager.getStoreWeixinConfig(presaleCouponOrder.getStoreId());
        PaymentOrderRecord log = paymentOrderRecordManager.create(order, PresaleCouponOrderPaymentType.WECHAT_PAY.real(), PaymentOrderRecordOperType.REFUND_CHECK);
        log.setPaymentType(PresaleCouponOrderPaymentType.WECHAT_PAY.real());
        try {
            String refundNo = "r-" + order.getBizType() + '-' + order.getOrderNo();
            WxPayRefundQueryResult result = weChatPayExternalService.getRefundResult(refundNo, storeWeixinConfig.getMchId());

            result.getRefundRecords().forEach(record -> {
                // 检查支付订单的状态, 并生成相应记录
            	log.setPaymentType(PresaleCouponOrderPaymentType.WECHAT_PAY.real());
            	log.setCurrencyType(order.getPaymentCurrencyType());
            	log.setTransAmount(BigDecimalUtil.centToDollar(BigDecimal.valueOf(record.getRefundFee())));
            	log.setTransNo(record.getRefundId());

                if (record.getRefundStatus().equalsIgnoreCase("PROCESSING")) {
                    // 用户支付中
                	log.setResultStatus(PaymentOrderRecordResultStatus.ONGOING.real());
                } else if (record.getRefundStatus().equalsIgnoreCase("SUCCESS")) {
                    // 用户支付成功
                	log.setTransTime(
                            DateUtil.parseToDate(DateUtil.DEFAULT_DATETIME_FORMAT, record.getRefundSuccessTime()));
                	log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
                } else {
                    // 其他原因全为失败
                	log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
                }

                // 记录调用详情
                paymentOrderRecordManager.add(log, updateUser);

                paymentOrderManager.updateRefundStatusAndNotify(order, log, updateUser);
            });

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            WxPayApiData data = weChatPayExternalService.getWxApiData();
            if (Check.notNull(data)) {
                log.setRequest(data.getRequestData());
                log.setResponse(data.getResponseData());
                log.setErrorMessage(data.getExceptionMsg());
            }
            // 记录调用详情
            paymentOrderRecordManager.add(log, updateUser);
        }
    }
    
    /**
     * 
     * 企业付款给用户
     * @param userId
     * @param partnerTradeNo
     * @param amount
     * @param description
     */
    public void entPay(StoreWeixinConfig weixinConfig, Integer userId, String partnerTradeNo, BigDecimal amount, String description) {
    	// 获取用户的openId
        List<ConsumerWeiXinMember> weixinList = consumerMemberManager.queryWeiXinByUserId(userId);
        BizAssert.isTrue(Check.notNullOrEmpty(weixinList), ResultCode.BIZ.build(11), "用户未绑定微信, 无法使用企业付款");
        try {
			wechatEntPayExternalService.entPay(weixinList.get(0).getOpenId(), 
				partnerTradeNo, 
				BigDecimalUtil.dollarToCent(amount).intValue(), 
				description,
				weixinConfig);
		} catch (WxPayException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
    }
}
